﻿using System;
using System.Collections.Generic;
using System.Windows;
using System.Linq;
using System.Windows.Data;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Media.Effects;
using System.Windows.Media.Media3D;
using System.Windows.Controls;
using System.Windows.Shapes;
using System.Windows.Controls.Primitives;
using Bling.Properties;
using DynamicShader.D3DX;
using Bling.DSL;
using Bling.WPF;
using Bling.Shaders;
using Bling.Util;
using linq = Microsoft.Linq.Expressions;

namespace Bling.DSL {
  public static partial class Defs {
    internal static readonly Bling.Shaders.BrushInput Input0 = new Bling.Shaders.BrushInput();
    internal static readonly Bling.Shaders.CurrentPoint UV0 = new Bling.Shaders.CurrentPoint();
    /// <summary>
    /// Input texture of pixel shader.
    /// </summary>
    public static BrushBl Input { get { return Input0.Bl(); } }
    /// <summary>
    /// Current cooridinate of pixel being shaded.
    /// </summary>
    public static PointBl UV { get { return UV0.Bl(); } }
    /// <summary>
    /// Current color of pixel being shaded.
    /// </summary>
    public static ColorBl At { get { return Input[UV]; } }
  }
}
namespace Bling.Shaders {
  public class BrushInput : Expr<Brush> {
    public override Eval<EVAL>.Info<Brush> Eval<EVAL>(Eval<EVAL> txt) {
      if (txt is ShaderEval) {
        return (Eval<EVAL>.Info<Brush>)(object)new ShaderEval.MyInfo<Brush>() {
          IsShader = true,
          Code = ToString(),
        };
      }
      if (txt is BaseShaderEval<EVAL>) {
        return (Eval<EVAL>.Info<Brush>)(object)((BaseShaderEval<EVAL>)txt).Eval(this);
      }
      if (txt is TranslateEval<EVAL>) {
        return ((TranslateEval<EVAL>)txt).NoTranslation(this);
      }
      throw new NotSupportedException();
    }
    public override string ToString() {
      return "implicitInput";
    }
    public override bool HasCurrentValue {
      get { return false; }
    }
  }
  public interface BaseShaderEval<EVAL> where EVAL : Eval<EVAL> {
    Eval<EVAL>.Info<Point> Eval(CurrentPoint pt);
    Eval<EVAL>.Info<Brush> Eval(BrushInput input);
  }
  public abstract class CurrentPointEval : ApplyEval<Point>, BaseShaderEval<ApplyEval<Point>> {
    public Info<Point> Eval(CurrentPoint pt) {
      var result = new MyInfo<Point>() { Value = p => p };
      return (Info<Point>)(object)result;
    }
    public abstract Info<Brush> Eval(BrushInput input);
  }
  public static class ShaderAlgorithms {
    public static Point3DBl Sample(BrushBl input, PointBl center, DoubleBl radius, int steps) {
      var dA = RadianAngleBl.MaxAngle / steps;
      var Angle = 0.ToPI();
      var result = new Point3DBl(0, 0, 0);
      for (int i = 0; i < steps; i++) {
        result += input[center + Angle.ToPoint() * radius].ScRGB;
        Angle += dA;
      }
      return result;
    }
    public static Point3DBl Transfer(Point3DBl original, Point3DBl other) {
      var rgb = new DoubleBl[3];
      for (int i = 0; i < 3; i++) {
        rgb[i] = Transfer(original[i], other[i]);
      }
      return new Point3DBl(rgb[0], rgb[1], rgb[2]);
    }
    public static DoubleBl Transfer(DoubleBl original, DoubleBl other) {
      var x = original;
      var y = other;
      var z = (x < y - 0.1).Condition(0.1, (x > y - 0.002).Condition(-0.002, y - x));
      x = x + z;
      return x;
    }
    public static ColorBl Diffusion(BrushBl input, PointBl uv, DoubleBl radius, int SegmentCount) {
      Point3DBl rgb = Sample(input, uv, radius, SegmentCount);
      var color0 = Transfer(input[uv].ScRGB, rgb);
      return ColorBl.FromScRGB(1, color0);
    }


  }

  public class CurrentPoint : Expr<Point> {
    public override Eval<EVAL>.Info<Point> Eval<EVAL>(Eval<EVAL> txt) {
      if (txt is ShaderEval) {
        return (Eval<EVAL>.Info<Point>)(object)new ShaderEval.MyInfo<Point>() {
          IsShader = true,
          Code = ToString(),
        };
      } else if (txt is BaseShaderEval<EVAL>) {
        return ((BaseShaderEval<EVAL>)txt).Eval(this);
      } else if (txt is TranslateEval<EVAL>) {
        return ((TranslateEval<EVAL>)txt).NoTranslation(this);
      }

      throw new NotImplementedException();
    }
    public override string ToString() {
      return "uv";
    }
    public override bool HasCurrentValue {
      get {
        return false;
      }
    }
  }
  static class ShaderExtensions {
    public static bool IsBrush(this Type t) {
      return t == typeof(Brush) || t.IsSubclassOf(typeof(Brush));
    }
  }

  public class ShaderEval : Eval<ShaderEval>, DependencyPropertyEval<ShaderEval>, ParameterEval<ShaderEval> {
    public readonly Dictionary<Expr, int> Temps = new Dictionary<Expr, int>();
    public readonly Dictionary<Expr, int> Registers = new Dictionary<Expr, int>();
    public readonly Dictionary<Expr<Brush>, int> Inputs = new Dictionary<Expr<Brush>, int>();
    public readonly System.IO.StringWriter TempBuffer = new System.IO.StringWriter();
    public ShaderEval() { }

    public class MyInfo<T> : Info<T> {
      public bool IsShader;
      public string Code;
    }
    public override Eval<ShaderEval>.Info<S> UpCast<T, S>(Eval<ShaderEval>.Info<T> info) {
      return new MyInfo<S>() { IsShader = ((MyInfo<T>)info).IsShader, Code = ((MyInfo<T>)info).Code };
    }

    public override Info<T> Constant<T>(Constant<T> constant) {
      if (typeof(T) == typeof(Color)) {
        var color = (Color)(object)constant.Value;
        return ArityX<T>((DoubleBl)color.ScA, (DoubleBl)color.ScR, (DoubleBl)color.ScG, (DoubleBl)color.ScB);
      } else if (typeof(T) == typeof(Point)) {
        var p = (Point)(object)constant.Value;
        return ArityX<T>((DoubleBl)p.X, (DoubleBl)p.Y);
      } else if (typeof(T) == typeof(Point3D)) {
        var p = (Point3D)(object)constant.Value;
        return ArityX<T>((DoubleBl)p.X, (DoubleBl)p.Y, (DoubleBl) p.Z);
      } else if (typeof(T).IsBrush()) {
        var cnt = new Constant<Brush>((Brush) (object)constant.Value);
        return new MyInfo<T>() { IsShader = true, Code = MakeInput(cnt) };
      } else if (typeof(T) == typeof(bool)) {
        var b = (bool)(object)constant.Value;
        return new MyInfo<T>() { IsShader = false, Code = (b ? "1" : "0") };
      } else return new MyInfo<T>() { IsShader = false, Code = constant.Value.ToString() };
    }
    public Info<T> Parameter<T>(ParameterExpr<T> param) {
      if (param is Expr<Brush>) {
        return new MyInfo<T>() { IsShader = true, Code = MakeInput((Expr<Brush>)(object)param) };
      }
      return new MyInfo<T>() { IsShader = true, Code = MakeRegister(param) };
    }

    private string TypeString(Type type) {
      if (type == typeof(double) || type == typeof(int) || type == typeof(bool)) return "float";
      if (type == typeof(Point)) return "float2";
      if (type == typeof(Point3D)) return "float3";
      if (type == typeof(Color)) return "float4";
      if (type.IsBrush()) throw new NotSupportedException();
      throw new NotSupportedException();
    }

    private string MakeTemp(Expr op, string Code) {
      int index = Temps.Count;
      // define temporary. 
      TempBuffer.WriteLine("  " + TypeString(op.TypeOfT) + " t" + index + " = " + Code + ";");
      Code = "t" + index;
      Temps[op] = index;
      return Code;
    }
    private string MakeRegister(Expr op) {
      if (Registers.ContainsKey(op)) return "r" + Registers[op];
      if (op is UnaryOperation && ((UnaryOperation) op).BaseOperator.Marker is Markers.UnaryAccessOperator) {
        var reg = MakeRegister(((UnaryOperation)op).BaseValue);
        return ((UnaryOperator)((UnaryOperation)op).BaseOperator).BaseShader(reg);
      }
      var index = Registers.Count;
      Registers[op] = index;
      return "r" + index;
    }
    private string MakeInput(Expr<Brush> op) {
      if (Inputs.ContainsKey(op)) return "i" + Inputs[op];
      var index = Inputs.Count;
      Inputs[op] = index;
      return "i" + index;
    }


    public override Info<T> Operate<S, T>(Operation<S, T> op) {
      if (Temps.ContainsKey(op)) return new MyInfo<T>() { IsShader = true, Code = "t" + Temps[op] };
      var result = (MyInfo<S>)op.Value.Eval(this);
      if (!result.IsShader && result.Code == null) return new MyInfo<T>() { IsShader = false };
      string Code = result.Code;
      string CodeOriginal = Code;

      Code = op.Op.Shader(Code);

      if (result.IsShader && Code != CodeOriginal) Code = MakeTemp(op, Code);
      return new MyInfo<T>() { IsShader = result.IsShader, Code = "(" + Code + ")" };
    }
    public override Info<U> Operate<S, T, U>(Operation<S, T, U> op) {
      if (Temps.ContainsKey(op)) return new MyInfo<U>() { IsShader = true, Code = "t" + Temps[op] };
      var result1 = (MyInfo<S>)op.Value1.Eval(this);
      var result2 = (MyInfo<T>)op.Value2.Eval(this);
      // push it up!
      if (!result1.IsShader && !result2.IsShader &&
          (result1.Code == null || result2.Code == null)) return new MyInfo<U>() { IsShader = false };
      {
        if (result1.Code == null) {
          (!result1.IsShader).Assert();
          result1.Code = MakeRegister(op.Value1);
        }
        if (result2.Code == null) {
          (!result2.IsShader).Assert();
          result2.Code = MakeRegister(op.Value2);
        }
      }
      // XXX: Do binary op here!
      string Code = op.Op.Shader(result1.Code, result2.Code);
      if (result1.IsShader || result2.IsShader) Code = MakeTemp(op, Code);
      return new MyInfo<U>() { IsShader = result1.IsShader || result2.IsShader, Code = "(" + Code + ")" };
    }
    public override Eval<ShaderEval>.Info<V> Operate<S, T, U, V>(Operation<S, T, U, V> op) {
      if (Temps.ContainsKey(op)) return new MyInfo<V>() { IsShader = true, Code = "t" + Temps[op] };
      var result1 = (MyInfo<S>)op.Value1.Eval(this);
      var result2 = (MyInfo<T>)op.Value2.Eval(this);
      var result3 = (MyInfo<U>)op.Value3.Eval(this);
      if (!result1.IsShader && !result2.IsShader && !result3.IsShader &&
          (result1.Code == null || result2.Code == null || result3.Code == null))
        return new MyInfo<V>() { IsShader = false };
      {
        if (result1.Code == null) {
          (!result1.IsShader).Assert();
          result1.Code = MakeRegister(op.Value1);
        }
        if (result2.Code == null) {
          (!result2.IsShader).Assert();
          result2.Code = MakeRegister(op.Value2);
        }
        if (result3.Code == null) {
          (!result3.IsShader).Assert();
          result3.Code = MakeRegister(op.Value3);
        }
      }
      string Code = op.Op.Shader(result1.Code, result2.Code, result3.Code);
      if (result1.IsShader || result2.IsShader || result3.IsShader) Code = MakeTemp(op, Code);
      return new MyInfo<V>() { IsShader = result1.IsShader || result2.IsShader || result3.IsShader, Code = "(" + Code + ")" };
    }
    public Info<T> ArityX<T>(params Expr<double>[] Values) {
      var results = new MyInfo<double>[Values.Length];
      bool AllNotShader = true;
      bool SomeCodeNull = false;
      bool AnyIsShader = false;
      for (int i = 0; i < Values.Length; i++) {
        results[i] = ((MyInfo<double>)Values[i].Eval(this));
        AllNotShader = AllNotShader && !results[i].IsShader;
        AnyIsShader = AnyIsShader || results[i].IsShader;
        SomeCodeNull = SomeCodeNull || results[i].Code == null;
      }
      if (AllNotShader && SomeCodeNull) return new MyInfo<T>() { IsShader = false };
      for (int i = 0; i < Values.Length; i++)
        if (results[i].Code == null) results[i].Code = MakeRegister(Values[i]);
      // Now...construct tuple. Easy since everything is a float tuple.
      if (typeof(T) == typeof(Color)) { // swap 0 and 3
        var t0 = results[0];
        for (int i = 0; i < 3; i++) // move everyone down by one.
          results[i] = results[i + 1];
        results[3] = t0;
      }

      string Code = "float" + Values.Length + "(";
      for (int i = 0; i < Values.Length; i++)
        Code = Code + (i == 0 ? "" : ", ") + results[i].Code;
      Code = Code + ")";
      return new MyInfo<T>() { IsShader = AnyIsShader, Code = Code };
    }

    public override Info<T> ArityX<T, ARITY>(ArityInfo<T, ARITY>.ArityExpr value) {
      return ArityX<T>(value.Values.ToArray());

    }
    public Info<T> PropertyValue<T>(DependencyPropertyValue<T> value) {
      if (typeof(T).IsBrush()) {
        return new MyInfo<T>() { IsShader = true, Code = MakeInput((PropertyValue<Brush>)(object)value) };
      } else return new MyInfo<T>() { IsShader = false }; // no code.
    }


    /*
    public BaseLiftedShaderEffect Generate(Value<Color> value) {
      var shader = GenerateA(value);
      BindNow(shader);
      return shader;
    }
     */
    public PixelShader GenerateA(Expr<Color> value) {
      var color = (MyInfo<Color>)value.Eval(this);
      if (!color.IsShader && color.Code == null) {
        color.Code = MakeRegister(value);
        color.IsShader = true;

        //throw new NotSupportedException();
      }
      string allCode;
      {
        System.IO.StringWriter Final = new System.IO.StringWriter();
        Final.WriteLine("sampler2D implicitInput : register(S0);");
        foreach (var input in Inputs) // doesn't include input property!
          Final.WriteLine("sampler2D i" + (input.Value) + " : register(S" + (input.Value + 1) + ");");
        foreach (var reg in Registers)
          Final.WriteLine(TypeString(reg.Key.TypeOfT) + " r" + reg.Value + " : register(C" + reg.Value + ");");
        Final.WriteLine("float4 main(float2 uv : TEXCOORD) : COLOR");
        Final.WriteLine("{");
        Final.Write(TempBuffer.ToString());
        Final.Write("  return " + color.Code + ";");
        Final.WriteLine();
        Final.WriteLine("}");
        allCode = Final.ToString();
      }
      Console.WriteLine(allCode);
      PixelShader pixelShader;
      var effect = DynamicShader.D3DX.CompiledEffect.FromString(allCode, CompilerOptions.None);
      if (effect.CompileSucceeded) {
        var shader0 = effect.CompileShader("main", "ps_2_0", CompilerOptions.None);
        if (shader0.CompileSucceeded) {
          pixelShader = new PixelShader();
          pixelShader.SetStreamSource(new System.IO.MemoryStream(shader0.Data));
        } else throw new ArgumentException("COMPILE FAILED " + shader0.ErrorMessage);
      } else {
        throw new ArgumentException("PARSE FAILED " + effect.ErrorMessage);
      }
      return pixelShader;
    }
    public BlingShaderEffect GenerateB(PixelShader pixelShader) {
      var Types = new Type[Registers.Count + Inputs.Count];
      foreach (var r in Registers) {
        if (r.Key.TypeOfT == typeof(bool))
          Types[r.Value] = typeof(double);
        else Types[r.Value] = r.Key.TypeOfT;
      }
      for (int i = 0; i < Inputs.Count; i++)
        Types[Registers.Count + i] = typeof(Brush);
      var shader = BlingShaderEffect.MakeShader(Types);
      shader.SetShader(pixelShader);
      foreach (var r in Registers)
        shader.Bind(r.Value, r.Key);
      foreach (var r in Inputs)
        shader.Bind(r.Value + Registers.Count, r.Key);
      return shader;
    }
    public Func<T, BlingShaderEffect> GenerateC<T>(PixelShader pixelShader, ParameterExpr<T> Argument) {
      var Types = new Type[Registers.Count + Inputs.Count];
      foreach (var r in Registers) {
        if (r.Key.TypeOfT == typeof(bool))
          Types[r.Value] = typeof(double);
        else Types[r.Value] = r.Key.TypeOfT;
      }
      for (int i = 0; i < Inputs.Count; i++)
        Types[Registers.Count + i] = typeof(Brush);
      // instruction for creating a new shader. 
      Type ShaderType = BlingShaderEffect.ShaderType(Types);
      {
        var c = ShaderType.GetConstructor(new Type[] { });
        var x = c.Invoke(new object[] {});

      }

      Action<T, BlingShaderEffect> BindAll;
      {
        var UseShaderType = ShaderType.BaseType;
        List<Action<T, BlingShaderEffect>> Binds = new List<Action<T, BlingShaderEffect>>();
        foreach (var r in Registers)
          Binds.Add(BlingShaderEffect.Bind<T>(UseShaderType, r.Value, r.Key, Argument));

        foreach (var r in Inputs)
          Binds.Add(BlingShaderEffect.Bind<T>(UseShaderType, r.Value + Registers.Count, r.Key, Argument));

        var Binds0 = Binds.ToArray();
        BindAll = (t, shader0) => {
          foreach (var b in Binds0) b(t, shader0);
        };
      }
      // create the shader
      var shader = linq.Expression.Parameter(typeof(BlingShaderEffect), "Shader");
      List<linq.Expression> expressions = new List<Microsoft.Linq.Expressions.Expression>();
      {
        linq.Expression newShader = linq.Expression.New(ShaderType);
        newShader = linq.Expression.Assign(shader, newShader);
        expressions.Add(newShader);
      }
      {
        var setShader = linq.Expression.Call(shader, "SetShader", new Type[] { }, linq.Expression.Constant(pixelShader, typeof(PixelShader)));
        expressions.Add(setShader);
      }
      var param = linq.Expression.Parameter(typeof(T), "paramT");
      expressions.Add(linq.Expression.Invoke(linq.Expression.Constant(BindAll, typeof(Action<T, BlingShaderEffect>)), param, shader));
      expressions.Add(shader);
      var block = linq.Expression.Block(typeof(BlingShaderEffect), new linq.ParameterExpression[] { shader }, expressions);
      var funcB = linq.Expression.Lambda<Microsoft.Func<T, BlingShaderEffect>>(block, param).Compile();
      return t => funcB(t);
    }
    public Func<T, S, BlingShaderEffect> GenerateC<T,S>(PixelShader pixelShader, ParameterExpr<T> ArgumentA, ParameterExpr<S> ArgumentB) {
      var Types = new Type[Registers.Count + Inputs.Count];
      foreach (var r in Registers) {
        if (r.Key.TypeOfT == typeof(bool))
          Types[r.Value] = typeof(double);
        else Types[r.Value] = r.Key.TypeOfT;
      }
      for (int i = 0; i < Inputs.Count; i++)
        Types[Registers.Count + i] = typeof(Brush);
      // instruction for creating a new shader. 
      Type ShaderType = BlingShaderEffect.ShaderType(Types);
      {
        var c = ShaderType.GetConstructor(new Type[] { });
        var x = c.Invoke(new object[] { });

      }

      Action<T, S, BlingShaderEffect> BindAll;
      {
        var UseShaderType = ShaderType.BaseType;
        List<Action<T, S, BlingShaderEffect>> Binds = new List<Action<T, S, BlingShaderEffect>>();
        foreach (var r in Registers)
          Binds.Add(BlingShaderEffect.Bind<T,S>(UseShaderType, r.Value, r.Key, ArgumentA, ArgumentB));

        foreach (var r in Inputs)
          Binds.Add(BlingShaderEffect.Bind<T,S>(UseShaderType, r.Value + Registers.Count, r.Key, ArgumentA, ArgumentB));

        var Binds0 = Binds.ToArray();
        BindAll = (t, s, shader0) => {
          foreach (var b in Binds0) b(t, s, shader0);
        };
      }
      // create the shader
      var shader = linq.Expression.Parameter(typeof(BlingShaderEffect), "Shader");
      List<linq.Expression> expressions = new List<Microsoft.Linq.Expressions.Expression>();
      {
        linq.Expression newShader = linq.Expression.New(ShaderType);
        newShader = linq.Expression.Assign(shader, newShader);
        expressions.Add(newShader);
      }
      {
        var setShader = linq.Expression.Call(shader, "SetShader", new Type[] { }, linq.Expression.Constant(pixelShader, typeof(PixelShader)));
        expressions.Add(setShader);
      }
      var paramA = linq.Expression.Parameter(typeof(T), "paramA");
      var paramB = linq.Expression.Parameter(typeof(S), "paramB");
      expressions.Add(linq.Expression.Invoke(linq.Expression.Constant(BindAll, typeof(Action<T, S, BlingShaderEffect>)), paramA, paramB, shader));
      expressions.Add(shader);
      var block = linq.Expression.Block(typeof(BlingShaderEffect), new linq.ParameterExpression[] { shader }, expressions);
      var funcB = linq.Expression.Lambda<Microsoft.Func<T, S, BlingShaderEffect>>(block, paramA, paramB).Compile();
      return (t,s) => funcB(t,s);
    }
  }
  /// <summary>
  /// Provides a custom bitmap effect by using a System.Windows.Media.Effects.PixelShader.
  /// </summary>
  public abstract class BlingShaderEffect : ShaderEffect {
    internal Dictionary<Expr, int> Registers;

    public abstract DependencyProperty GetProperty(int n);
    public abstract BlingShaderEffect MakeCopy();
    public void SetShader(PixelShader shader) {
      this.PixelShader = shader;
    }
    public static DependencyProperty PixelShaderProperty0 {
      get { return ShaderEffect.PixelShaderProperty; }
    }

    public ShaderRenderMode ShaderRenderMode {
      get { return PixelShader.ShaderRenderMode; }
      set { PixelShader.ShaderRenderMode = value; }
    }
    private class MyGeneralTransform : GeneralTransform {
      internal Func<Point, Point> F;
      protected override Freezable CreateInstanceCore() {
        return this;
      }
      public override GeneralTransform Inverse {
        get { 
          return this; 
        }
      }
      public override bool TryTransform(Point inPoint, out Point result) {
        result = F(inPoint);
        return true;
      }
      public override Rect TransformBounds(Rect rect) {
        return new Rect(F(rect.TopLeft), F(rect.BottomRight));
      }
    }
    protected override GeneralTransform EffectMapping {
      get {
        if (CustomEffectMapping != null) {
          return new MyGeneralTransform() { F = CustomEffectMapping };
        }
        return base.EffectMapping;
      }
    }
    internal Func<Point, Point> CustomEffectMapping { get; set; }

    public PointBl PaddingLeftTop {
      get {
        return new PointBl(this.PaddingLeft, this.PaddingTop);
      }
      set {
        this.PaddingLeft = value.X.CurrentValue;
        this.PaddingTop = value.Y.CurrentValue;
      }

    }
    public PointBl PaddingRightBottom {
      get {
        return new PointBl(this.PaddingRight, this.PaddingBottom);
      }
      set {
        this.PaddingRight = value.X.CurrentValue;
        this.PaddingBottom = value.Y.CurrentValue;
      }
    }
    public DoubleBl Padding {
      get { return this.PaddingLeft; }
      set {
        this.PaddingLeft = value.CurrentValue;
        this.PaddingTop = value.CurrentValue;
        this.PaddingRight = value.CurrentValue;
        this.PaddingBottom = value.CurrentValue;
      }
    }

    public Expr<T> GetX<T>(int idx) {
      return this.Bl<T>(GetProperty(idx));
    }
    public Expr this[int idx] {
      get {
        idx = idx + 1;
        var method = this.GetType().GetMethod("GetX", new Type[] { typeof(int) });
        method = method.MakeGenericMethod(GetProperty(idx).PropertyType);
        return (Expr)method.Invoke(this, new object[] { idx });
      }
    }
    public void Bind(int idx, Expr RHS) {
      if (RHS.TypeOfT == typeof(bool)) {
        // convert to float.
        BoolBl b = ((Expr<bool>)RHS);
        RHS = b.Condition(1d, 0d);
      }
      var LHS = this[idx];
      (LHS.TypeOfT == RHS.TypeOfT).Assert();
      var method = this.GetType().GetMethod("DoBindX");
      method = method.MakeGenericMethod(LHS.TypeOfT);
      method.Invoke(this, new object[] { LHS, RHS });
    }
    public void DoBindX<T>(Expr<T> LHS, Expr<T> RHS) {
      if (!LHS.Solve(new BindAssign(), RHS)) throw new NotSupportedException();
    }

    public static Action<S, BlingShaderEffect> DoBindXX<T,S>(DependencyProperty dp, Expr<T> RHS, ParameterExpr<S> p) {
      var eval = new LinqBindEval() { Delay = true };
      eval.Parameters = new Bling.Linq.ParameterContext();
      eval.Parameters.Add(p);
      // XXX: configure p!


      var RHS0 = ((LinqBindEval.MyInfo<T>) RHS.Eval(eval)).Value;
      RHS0 = eval.PopScope<T>(RHS0);

      var F0 = linq.Expression.Lambda<Microsoft.Func<object[], T>>(RHS0, eval.Args);
      var F2 = linq.Expression.Lambda<Microsoft.Func<S, Microsoft.Func<object[], T>>>(F0, eval.Parameters.Flush());
      var F3 = F2.Compile();
      // we need to hold on to that. 
      //var OrigBindings = eval.Binding.Bindings;
      var keys = eval.BindKeys;
      var properties = new PropertyPath[keys.Count];
      var targets = new Func<S,DependencyObject>[keys.Count];
      for (int i = 0; i < keys.Count; i++) {
        properties[i] = new PropertyPath(keys[i].Property);
        var f = linq.Expression.Lambda <Microsoft.Func<S, DependencyObject>>(keys[i].Target, eval.Parameters.Flush()).Compile();
        targets[i] = s => f(s);
      }
      return (s, shader) => {
        var F4 = F3(s);
        var Binding = new MultiBinding();
        // copy the bindings.
        for (int i = 0; i < properties.Length; i++) {
          Binding.Bindings.Add(new Binding() { Source = targets[i](s), Path = properties[i] });
        }
        Binding.Mode = BindingMode.OneWay;
        Binding.Converter = new BindConverter<T>() { F = args => F4(args), G = null };
        var e = BindingOperations.SetBinding(shader, dp, Binding);
      };
    }
    public static Action<S, U, BlingShaderEffect> DoBindYY<T, S, U>(DependencyProperty dp, Expr<T> RHS, ParameterExpr<S> pA, ParameterExpr<U> pB) {
      var eval = new LinqBindEval() { Delay = true };
      eval.Parameters = new Bling.Linq.ParameterContext();
      eval.Parameters.Add(pA);
      eval.Parameters.Add(pB);
      // XXX: configure p!

      var RHS0 = ((LinqBindEval.MyInfo<T>)RHS.Eval(eval)).Value;
      RHS0 = eval.PopScope<T>(RHS0);

      var F0 = linq.Expression.Lambda<Microsoft.Func<object[], T>>(RHS0, eval.Args);
      var F2 = linq.Expression.Lambda<Microsoft.Func<S, U, Microsoft.Func<object[], T>>>(F0, eval.Parameters.Flush());
      var F3 = F2.Compile();
      // we need to hold on to that. 
      //var OrigBindings = eval.Binding.Bindings;
      var keys = eval.BindKeys;
      var properties = new PropertyPath[keys.Count];
      var targets = new Func<S, U, DependencyObject>[keys.Count];
      for (int i = 0; i < keys.Count; i++) {
        properties[i] = new PropertyPath(keys[i].Property);
        var f = linq.Expression.Lambda<Microsoft.Func<S, U, DependencyObject>>(keys[i].Target, eval.Parameters.Flush()).Compile();
        targets[i] = (s,u) => f(s,u);
      }
      return (s, u, shader) => {
        var F4 = F3(s, u);
        var Binding = new MultiBinding();
        // copy the bindings.
        for (int i = 0; i < properties.Length; i++) {
          Binding.Bindings.Add(new Binding() { Source = targets[i](s, u), Path = properties[i] });
        }
        Binding.Mode = BindingMode.OneWay;
        Binding.Converter = new BindConverter<T>() { F = args => F4(args), G = null };
        var e = BindingOperations.SetBinding(shader, dp, Binding);
      };
    }
    public static Action<T, BlingShaderEffect> Bind<T>(Type ShaderType, int idx, Expr RHS, ParameterExpr<T> Argument) {
      if (RHS.TypeOfT == typeof(bool)) {
        // convert to float.
        BoolBl b = ((Expr<bool>)RHS);
        RHS = b.Condition(1d, 0d);
      }
      

      var AllProperties = (List<DependencyProperty>) ShaderType.GetField("AllProperties").GetValue(null);
      DependencyProperty dp = AllProperties[idx + 1];
      var method = typeof(BlingShaderEffect).GetMethod("DoBindXX");
      method = method.MakeGenericMethod(RHS.TypeOfT, typeof(T));
      return (Action<T, BlingShaderEffect>) method.Invoke(null, new object[] { dp, RHS, Argument });
    }
    public static Action<T, S, BlingShaderEffect> Bind<T, S>(Type ShaderType, int idx, Expr RHS, ParameterExpr<T> ArgumentA, ParameterExpr<S> ArgumentB) {
      if (RHS.TypeOfT == typeof(bool)) {
        // convert to float.
        BoolBl b = ((Expr<bool>)RHS);
        RHS = b.Condition(1d, 0d);
      }
      var AllProperties = (List<DependencyProperty>)ShaderType.GetField("AllProperties").GetValue(null);
      DependencyProperty dp = AllProperties[idx + 1];
      var method = typeof(BlingShaderEffect).GetMethod("DoBindYY");
      method = method.MakeGenericMethod(RHS.TypeOfT, typeof(T), typeof(S));
      return (Action<T, S, BlingShaderEffect>)method.Invoke(null, new object[] { dp, RHS, ArgumentA, ArgumentB });
    }
    public static BlingShaderEffect MakeShader(Type[] Types) {
      var shader = ShaderType(Types);
      var c = shader.GetConstructor(new Type[] { });
      return (BlingShaderEffect)c.Invoke(new object[] { });
    }
    public static Type ShaderType(Type[] Types) {
      if (Types.Length >= ArgShaders.Length) throw new NotSupportedException("Too many shader parameters");
      var shader = ArgShaders[Types.Length];
      if (Types.Length > 0)
        shader = shader.MakeGenericType(Types);
      return shader;
      //return linq.Expression.New(shader);
    }
    private static Type[] ArgShaders = new Type[] {
      typeof(ArgShader0),
      typeof(ArgShader1<>),
      typeof(ArgShader2<,>),
      typeof(ArgShader3<,,>),
      typeof(ArgShader4<,,,>),
      typeof(ArgShader5<,,,,>),
      typeof(ArgShader6<,,,,,>),
      typeof(ArgShader7<,,,,,,>),
      typeof(ArgShader8<,,,,,,,>),
      typeof(ArgShader9<,,,,,,,,>),
      typeof(ArgShader10<,,,,,,,,,>),
      typeof(ArgShader11<,,,,,,,,,,>),
      typeof(ArgShader12<,,,,,,,,,,,>),
      typeof(ArgShader13<,,,,,,,,,,,,>),
      typeof(ArgShader14<,,,,,,,,,,,,,>),
      typeof(ArgShader15<,,,,,,,,,,,,,,>),
    };

  }
  public abstract class LiftedShaderEffect<SELF> : BlingShaderEffect where SELF : LiftedShaderEffect<SELF>, new() {
    internal static readonly List<DependencyProperty> Registers = new List<DependencyProperty>();
    internal static readonly List<DependencyProperty> Inputs = new List<DependencyProperty>();
    public static readonly List<DependencyProperty> AllProperties = new List<DependencyProperty>();
    public override DependencyProperty GetProperty(int n) { return AllProperties[n]; }
    public static readonly DependencyProperty InputProperty = AddInput("Input");

    public static DependencyProperty GetPropertyS(int n) { return AllProperties[n]; }

    protected static DependencyProperty AddParameter<T>(string name, T defaultValue) {
      if (typeof(T).IsBrush()) throw new ArgumentException("Textures don't have defaults!");
      var dp = DependencyProperty.Register(name, typeof(T), typeof(SELF),
        new UIPropertyMetadata(defaultValue, PixelShaderConstantCallback(Registers.Count)));
      Registers.Add(dp);
      AllProperties.Add(dp);
      return dp;
    }
    protected static DependencyProperty AddParameter<T>(string name) {
      return AddParameter(name, typeof(T));
    }
    protected static DependencyProperty AddParameter(string name, Type type) {
      if (type.IsBrush()) return AddInput(name);
      var dp = DependencyProperty.Register(name, type, typeof(SELF),
        new UIPropertyMetadata(PixelShaderConstantCallback(Registers.Count)));
      Registers.Add(dp);
      AllProperties.Add(dp);
      return dp;
    }
    private static DependencyProperty AddInput(string name) {
      var dp = ShaderEffect.RegisterPixelShaderSamplerProperty(name, typeof(SELF), Inputs.Count);
      Inputs.Add(dp);
      AllProperties.Add(dp);
      return dp;
    }
    public override BlingShaderEffect MakeCopy() {
      var ret = new SELF();
      ret.PixelShader = this.PixelShader;
      return ret;
    }
    public LiftedShaderEffect() {
      foreach (var input in Inputs) UpdateShaderValue(input);
      foreach (var reg in Registers) UpdateShaderValue(reg);
    }
  }
  public class ArgShader0 : LiftedShaderEffect<ArgShader0> { }
  public class ArgShader1<T0> : LiftedShaderEffect<ArgShader1<T0>> {
    static ArgShader1() {
      AddParameter<T0>("Arg0");
    }
    public PropertyValue<T0> Parameter0 { get { return this.Bl<T0>(AllProperties[0 + 1]); } }
  }
  public class ArgShader2<T0, T1> : LiftedShaderEffect<ArgShader2<T0, T1>> {
    static ArgShader2() {
      AddParameter<T0>("Arg0");
      AddParameter<T1>("Arg1");
    }
    public PropertyValue<T0> Parameter0 { get { return this.Bl<T0>(AllProperties[0 + 1]); } }
    public PropertyValue<T1> Parameter1 { get { return this.Bl<T1>(AllProperties[1 + 1]); } }
  }
  public class ArgShader3<T0, T1, T2> : LiftedShaderEffect<ArgShader3<T0, T1, T2>> {
    static ArgShader3() {
      AddParameter<T0>("Arg0");
      AddParameter<T1>("Arg1");
      AddParameter<T2>("Arg2");
    }
    public PropertyValue<T0> Parameter0 { get { return this.Bl<T0>(AllProperties[0 + 1]); } }
    public PropertyValue<T1> Parameter1 { get { return this.Bl<T1>(AllProperties[1 + 1]); } }
    public PropertyValue<T2> Parameter2 { get { return this.Bl<T2>(AllProperties[2 + 1]); } }
  }
  public class ArgShader4<T0, T1, T2, T3> : LiftedShaderEffect<ArgShader4<T0, T1, T2, T3>> {
    static ArgShader4() {
      AddParameter<T0>("Arg0");
      AddParameter<T1>("Arg1");
      AddParameter<T2>("Arg2");
      AddParameter<T3>("Arg3");
    }
    public PropertyValue<T0> Parameter0 { get { return this.Bl<T0>(AllProperties[0 + 1]); } }
    public PropertyValue<T1> Parameter1 { get { return this.Bl<T1>(AllProperties[1 + 1]); } }
    public PropertyValue<T2> Parameter2 { get { return this.Bl<T2>(AllProperties[2 + 1]); } }
    public PropertyValue<T3> Parameter3 { get { return this.Bl<T3>(AllProperties[3 + 1]); } }
  }
  public class ArgShader5<T0, T1, T2, T3, T4> : LiftedShaderEffect<ArgShader5<T0, T1, T2, T3, T4>> {
    static ArgShader5() {
      AddParameter<T0>("Arg0");
      AddParameter<T1>("Arg1");
      AddParameter<T2>("Arg2");
      AddParameter<T3>("Arg3");
      AddParameter<T4>("Arg4");
    }
    public PropertyValue<T0> Parameter0 { get { return this.Bl<T0>(AllProperties[0 + 1]); } }
    public PropertyValue<T1> Parameter1 { get { return this.Bl<T1>(AllProperties[1 + 1]); } }
    public PropertyValue<T2> Parameter2 { get { return this.Bl<T2>(AllProperties[2 + 1]); } }
    public PropertyValue<T3> Parameter3 { get { return this.Bl<T3>(AllProperties[3 + 1]); } }
    public PropertyValue<T4> Parameter4 { get { return this.Bl<T4>(AllProperties[4 + 1]); } }
  }
  public class ArgShader6<T0, T1, T2, T3, T4, T5> : LiftedShaderEffect<ArgShader6<T0, T1, T2, T3, T4, T5>> {
    static ArgShader6() {
      AddParameter<T0>("Arg0");
      AddParameter<T1>("Arg1");
      AddParameter<T2>("Arg2");
      AddParameter<T3>("Arg3");
      AddParameter<T4>("Arg4");
      AddParameter<T5>("Arg5");
    }
    public PropertyValue<T0> Parameter0 { get { return this.Bl<T0>(AllProperties[0 + 1]); } }
    public PropertyValue<T1> Parameter1 { get { return this.Bl<T1>(AllProperties[1 + 1]); } }
    public PropertyValue<T2> Parameter2 { get { return this.Bl<T2>(AllProperties[2 + 1]); } }
    public PropertyValue<T3> Parameter3 { get { return this.Bl<T3>(AllProperties[3 + 1]); } }
    public PropertyValue<T4> Parameter4 { get { return this.Bl<T4>(AllProperties[4 + 1]); } }
    public PropertyValue<T5> Parameter5 { get { return this.Bl<T5>(AllProperties[5 + 1]); } }
  }
  public class ArgShader7<T0, T1, T2, T3, T4, T5, T6> : LiftedShaderEffect<ArgShader7<T0, T1, T2, T3, T4, T5, T6>> {
    static ArgShader7() {
      AddParameter<T0>("Arg0");
      AddParameter<T1>("Arg1");
      AddParameter<T2>("Arg2");
      AddParameter<T3>("Arg3");
      AddParameter<T4>("Arg4");
      AddParameter<T5>("Arg5");
      AddParameter<T6>("Arg6");
    }
    public PropertyValue<T0> Parameter0 { get { return this.Bl<T0>(AllProperties[0 + 1]); } }
    public PropertyValue<T1> Parameter1 { get { return this.Bl<T1>(AllProperties[1 + 1]); } }
    public PropertyValue<T2> Parameter2 { get { return this.Bl<T2>(AllProperties[2 + 1]); } }
    public PropertyValue<T3> Parameter3 { get { return this.Bl<T3>(AllProperties[3 + 1]); } }
    public PropertyValue<T4> Parameter4 { get { return this.Bl<T4>(AllProperties[4 + 1]); } }
    public PropertyValue<T5> Parameter5 { get { return this.Bl<T5>(AllProperties[5 + 1]); } }
    public PropertyValue<T6> Parameter6 { get { return this.Bl<T6>(AllProperties[6 + 1]); } }
  }
  public class ArgShader8<T0, T1, T2, T3, T4, T5, T6, T7> : LiftedShaderEffect<ArgShader8<T0, T1, T2, T3, T4, T5, T6, T7>> {
    static ArgShader8() {
      AddParameter<T0>("Arg0");
      AddParameter<T1>("Arg1");
      AddParameter<T2>("Arg2");
      AddParameter<T3>("Arg3");
      AddParameter<T4>("Arg4");
      AddParameter<T5>("Arg5");
      AddParameter<T6>("Arg6");
      AddParameter<T7>("Arg7");
    }
    public PropertyValue<T0> Parameter0 { get { return this.Bl<T0>(AllProperties[0 + 1]); } }
    public PropertyValue<T1> Parameter1 { get { return this.Bl<T1>(AllProperties[1 + 1]); } }
    public PropertyValue<T2> Parameter2 { get { return this.Bl<T2>(AllProperties[2 + 1]); } }
    public PropertyValue<T3> Parameter3 { get { return this.Bl<T3>(AllProperties[3 + 1]); } }
    public PropertyValue<T4> Parameter4 { get { return this.Bl<T4>(AllProperties[4 + 1]); } }
    public PropertyValue<T5> Parameter5 { get { return this.Bl<T5>(AllProperties[5 + 1]); } }
    public PropertyValue<T6> Parameter6 { get { return this.Bl<T6>(AllProperties[6 + 1]); } }
    public PropertyValue<T7> Parameter7 { get { return this.Bl<T7>(AllProperties[7 + 1]); } }
  }
  public class ArgShader9<T0, T1, T2, T3, T4, T5, T6, T7, T8> : LiftedShaderEffect<ArgShader9<T0, T1, T2, T3, T4, T5, T6, T7, T8>> {
    static ArgShader9() {
      AddParameter<T0>("Arg0");
      AddParameter<T1>("Arg1");
      AddParameter<T2>("Arg2");
      AddParameter<T3>("Arg3");
      AddParameter<T4>("Arg4");
      AddParameter<T5>("Arg5");
      AddParameter<T6>("Arg6");
      AddParameter<T7>("Arg7");
      AddParameter<T8>("Arg8");
    }
    public PropertyValue<T0> Parameter0 { get { return this.Bl<T0>(AllProperties[0 + 1]); } }
    public PropertyValue<T1> Parameter1 { get { return this.Bl<T1>(AllProperties[1 + 1]); } }
    public PropertyValue<T2> Parameter2 { get { return this.Bl<T2>(AllProperties[2 + 1]); } }
    public PropertyValue<T3> Parameter3 { get { return this.Bl<T3>(AllProperties[3 + 1]); } }
    public PropertyValue<T4> Parameter4 { get { return this.Bl<T4>(AllProperties[4 + 1]); } }
    public PropertyValue<T5> Parameter5 { get { return this.Bl<T5>(AllProperties[5 + 1]); } }
    public PropertyValue<T6> Parameter6 { get { return this.Bl<T6>(AllProperties[6 + 1]); } }
    public PropertyValue<T7> Parameter7 { get { return this.Bl<T7>(AllProperties[7 + 1]); } }
    public PropertyValue<T8> Parameter8 { get { return this.Bl<T8>(AllProperties[8 + 1]); } }
  }
  public class ArgShader10<T0, T1, T2, T3, T4, T5, T6, T7, T8, T9> : LiftedShaderEffect<ArgShader10<T0, T1, T2, T3, T4, T5, T6, T7, T8, T9>> {
    static ArgShader10() {
      AddParameter<T0>("Arg0");
      AddParameter<T1>("Arg1");
      AddParameter<T2>("Arg2");
      AddParameter<T3>("Arg3");
      AddParameter<T4>("Arg4");
      AddParameter<T5>("Arg5");
      AddParameter<T6>("Arg6");
      AddParameter<T7>("Arg7");
      AddParameter<T8>("Arg8");
      AddParameter<T9>("Arg9");
    }
    public PropertyValue<T0> Parameter0 { get { return this.Bl<T0>(AllProperties[0 + 1]); } }
    public PropertyValue<T1> Parameter1 { get { return this.Bl<T1>(AllProperties[1 + 1]); } }
    public PropertyValue<T2> Parameter2 { get { return this.Bl<T2>(AllProperties[2 + 1]); } }
    public PropertyValue<T3> Parameter3 { get { return this.Bl<T3>(AllProperties[3 + 1]); } }
    public PropertyValue<T4> Parameter4 { get { return this.Bl<T4>(AllProperties[4 + 1]); } }
    public PropertyValue<T5> Parameter5 { get { return this.Bl<T5>(AllProperties[5 + 1]); } }
    public PropertyValue<T6> Parameter6 { get { return this.Bl<T6>(AllProperties[6 + 1]); } }
    public PropertyValue<T7> Parameter7 { get { return this.Bl<T7>(AllProperties[7 + 1]); } }
    public PropertyValue<T8> Parameter8 { get { return this.Bl<T8>(AllProperties[8 + 1]); } }
    public PropertyValue<T9> Parameter9 { get { return this.Bl<T9>(AllProperties[9 + 1]); } }
  }
  public class ArgShader11<T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10> : LiftedShaderEffect<ArgShader11<T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10>> {
    static ArgShader11() {
      AddParameter<T0>("Arg0");
      AddParameter<T1>("Arg1");
      AddParameter<T2>("Arg2");
      AddParameter<T3>("Arg3");
      AddParameter<T4>("Arg4");
      AddParameter<T5>("Arg5");
      AddParameter<T6>("Arg6");
      AddParameter<T7>("Arg7");
      AddParameter<T8>("Arg8");
      AddParameter<T9>("Arg9");
      AddParameter<T10>("Arg10");
    }
    public PropertyValue<T0> Parameter0 { get { return this.Bl<T0>(AllProperties[0 + 1]); } }
    public PropertyValue<T1> Parameter1 { get { return this.Bl<T1>(AllProperties[1 + 1]); } }
    public PropertyValue<T2> Parameter2 { get { return this.Bl<T2>(AllProperties[2 + 1]); } }
    public PropertyValue<T3> Parameter3 { get { return this.Bl<T3>(AllProperties[3 + 1]); } }
    public PropertyValue<T4> Parameter4 { get { return this.Bl<T4>(AllProperties[4 + 1]); } }
    public PropertyValue<T5> Parameter5 { get { return this.Bl<T5>(AllProperties[5 + 1]); } }
    public PropertyValue<T6> Parameter6 { get { return this.Bl<T6>(AllProperties[6 + 1]); } }
    public PropertyValue<T7> Parameter7 { get { return this.Bl<T7>(AllProperties[7 + 1]); } }
    public PropertyValue<T8> Parameter8 { get { return this.Bl<T8>(AllProperties[8 + 1]); } }
    public PropertyValue<T9> Parameter9 { get { return this.Bl<T9>(AllProperties[9 + 1]); } }
    public PropertyValue<T10> Parameter10 { get { return this.Bl<T10>(AllProperties[10 + 1]); } }
  }

  public class ArgShader12<T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11> : LiftedShaderEffect<ArgShader12<T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11>> {
    static ArgShader12() {
      AddParameter<T0>("Arg0");
      AddParameter<T1>("Arg1");
      AddParameter<T2>("Arg2");
      AddParameter<T3>("Arg3");
      AddParameter<T4>("Arg4");
      AddParameter<T5>("Arg5");
      AddParameter<T6>("Arg6");
      AddParameter<T7>("Arg7");
      AddParameter<T8>("Arg8");
      AddParameter<T9>("Arg9");
      AddParameter<T10>("Arg10");
      AddParameter<T11>("Arg11");
    }
    public PropertyValue<T0> Parameter0 { get { return this.Bl<T0>(AllProperties[0 + 1]); } }
    public PropertyValue<T1> Parameter1 { get { return this.Bl<T1>(AllProperties[1 + 1]); } }
    public PropertyValue<T2> Parameter2 { get { return this.Bl<T2>(AllProperties[2 + 1]); } }
    public PropertyValue<T3> Parameter3 { get { return this.Bl<T3>(AllProperties[3 + 1]); } }
    public PropertyValue<T4> Parameter4 { get { return this.Bl<T4>(AllProperties[4 + 1]); } }
    public PropertyValue<T5> Parameter5 { get { return this.Bl<T5>(AllProperties[5 + 1]); } }
    public PropertyValue<T6> Parameter6 { get { return this.Bl<T6>(AllProperties[6 + 1]); } }
    public PropertyValue<T7> Parameter7 { get { return this.Bl<T7>(AllProperties[7 + 1]); } }
    public PropertyValue<T8> Parameter8 { get { return this.Bl<T8>(AllProperties[8 + 1]); } }
    public PropertyValue<T9> Parameter9 { get { return this.Bl<T9>(AllProperties[9 + 1]); } }
    public PropertyValue<T10> Parameter10 { get { return this.Bl<T10>(AllProperties[10 + 1]); } }
    public PropertyValue<T11> Parameter11 { get { return this.Bl<T11>(AllProperties[11 + 1]); } }
  }
  public class ArgShader13<T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12> : LiftedShaderEffect<ArgShader13<T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12>> {
    static ArgShader13() {
      AddParameter<T0>("Arg0");
      AddParameter<T1>("Arg1");
      AddParameter<T2>("Arg2");
      AddParameter<T3>("Arg3");
      AddParameter<T4>("Arg4");
      AddParameter<T5>("Arg5");
      AddParameter<T6>("Arg6");
      AddParameter<T7>("Arg7");
      AddParameter<T8>("Arg8");
      AddParameter<T9>("Arg9");
      AddParameter<T10>("Arg10");
      AddParameter<T11>("Arg11");
      AddParameter<T12>("Arg12");
    }
    public PropertyValue<T0> Parameter0 { get { return this.Bl<T0>(AllProperties[0 + 1]); } }
    public PropertyValue<T1> Parameter1 { get { return this.Bl<T1>(AllProperties[1 + 1]); } }
    public PropertyValue<T2> Parameter2 { get { return this.Bl<T2>(AllProperties[2 + 1]); } }
    public PropertyValue<T3> Parameter3 { get { return this.Bl<T3>(AllProperties[3 + 1]); } }
    public PropertyValue<T4> Parameter4 { get { return this.Bl<T4>(AllProperties[4 + 1]); } }
    public PropertyValue<T5> Parameter5 { get { return this.Bl<T5>(AllProperties[5 + 1]); } }
    public PropertyValue<T6> Parameter6 { get { return this.Bl<T6>(AllProperties[6 + 1]); } }
    public PropertyValue<T7> Parameter7 { get { return this.Bl<T7>(AllProperties[7 + 1]); } }
    public PropertyValue<T8> Parameter8 { get { return this.Bl<T8>(AllProperties[8 + 1]); } }
    public PropertyValue<T9> Parameter9 { get { return this.Bl<T9>(AllProperties[9 + 1]); } }
    public PropertyValue<T10> Parameter10 { get { return this.Bl<T10>(AllProperties[10 + 1]); } }
    public PropertyValue<T11> Parameter11 { get { return this.Bl<T11>(AllProperties[11 + 1]); } }
    public PropertyValue<T12> Parameter12 { get { return this.Bl<T12>(AllProperties[12 + 1]); } }
  }
  public class ArgShader14<T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13> : LiftedShaderEffect<ArgShader14<T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13>> {
    static ArgShader14() {
      AddParameter<T0>("Arg0");
      AddParameter<T1>("Arg1");
      AddParameter<T2>("Arg2");
      AddParameter<T3>("Arg3");
      AddParameter<T4>("Arg4");
      AddParameter<T5>("Arg5");
      AddParameter<T6>("Arg6");
      AddParameter<T7>("Arg7");
      AddParameter<T8>("Arg8");
      AddParameter<T9>("Arg9");
      AddParameter<T10>("Arg10");
      AddParameter<T11>("Arg11");
      AddParameter<T12>("Arg12");
      AddParameter<T13>("Arg13");
    }
    public PropertyValue<T0> Parameter0 { get { return this.Bl<T0>(AllProperties[0 + 1]); } }
    public PropertyValue<T1> Parameter1 { get { return this.Bl<T1>(AllProperties[1 + 1]); } }
    public PropertyValue<T2> Parameter2 { get { return this.Bl<T2>(AllProperties[2 + 1]); } }
    public PropertyValue<T3> Parameter3 { get { return this.Bl<T3>(AllProperties[3 + 1]); } }
    public PropertyValue<T4> Parameter4 { get { return this.Bl<T4>(AllProperties[4 + 1]); } }
    public PropertyValue<T5> Parameter5 { get { return this.Bl<T5>(AllProperties[5 + 1]); } }
    public PropertyValue<T6> Parameter6 { get { return this.Bl<T6>(AllProperties[6 + 1]); } }
    public PropertyValue<T7> Parameter7 { get { return this.Bl<T7>(AllProperties[7 + 1]); } }
    public PropertyValue<T8> Parameter8 { get { return this.Bl<T8>(AllProperties[8 + 1]); } }
    public PropertyValue<T9> Parameter9 { get { return this.Bl<T9>(AllProperties[9 + 1]); } }
    public PropertyValue<T10> Parameter10 { get { return this.Bl<T10>(AllProperties[10 + 1]); } }
    public PropertyValue<T11> Parameter11 { get { return this.Bl<T11>(AllProperties[11 + 1]); } }
    public PropertyValue<T12> Parameter12 { get { return this.Bl<T12>(AllProperties[12 + 1]); } }
    public PropertyValue<T13> Parameter13 { get { return this.Bl<T13>(AllProperties[13 + 1]); } }
  }
  public class ArgShader15<T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14> : LiftedShaderEffect<ArgShader15<T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14>> {
    static ArgShader15() {
      AddParameter<T0>("Arg0");
      AddParameter<T1>("Arg1");
      AddParameter<T2>("Arg2");
      AddParameter<T3>("Arg3");
      AddParameter<T4>("Arg4");
      AddParameter<T5>("Arg5");
      AddParameter<T6>("Arg6");
      AddParameter<T7>("Arg7");
      AddParameter<T8>("Arg8");
      AddParameter<T9>("Arg9");
      AddParameter<T10>("Arg10");
      AddParameter<T11>("Arg11");
      AddParameter<T12>("Arg12");
      AddParameter<T13>("Arg13");
      AddParameter<T14>("Arg14");
    }
    public PropertyValue<T0> Parameter0 { get { return this.Bl<T0>(AllProperties[0 + 1]); } }
    public PropertyValue<T1> Parameter1 { get { return this.Bl<T1>(AllProperties[1 + 1]); } }
    public PropertyValue<T2> Parameter2 { get { return this.Bl<T2>(AllProperties[2 + 1]); } }
    public PropertyValue<T3> Parameter3 { get { return this.Bl<T3>(AllProperties[3 + 1]); } }
    public PropertyValue<T4> Parameter4 { get { return this.Bl<T4>(AllProperties[4 + 1]); } }
    public PropertyValue<T5> Parameter5 { get { return this.Bl<T5>(AllProperties[5 + 1]); } }
    public PropertyValue<T6> Parameter6 { get { return this.Bl<T6>(AllProperties[6 + 1]); } }
    public PropertyValue<T7> Parameter7 { get { return this.Bl<T7>(AllProperties[7 + 1]); } }
    public PropertyValue<T8> Parameter8 { get { return this.Bl<T8>(AllProperties[8 + 1]); } }
    public PropertyValue<T9> Parameter9 { get { return this.Bl<T9>(AllProperties[9 + 1]); } }
    public PropertyValue<T10> Parameter10 { get { return this.Bl<T10>(AllProperties[10 + 1]); } }
    public PropertyValue<T11> Parameter11 { get { return this.Bl<T11>(AllProperties[11 + 1]); } }
    public PropertyValue<T12> Parameter12 { get { return this.Bl<T12>(AllProperties[12 + 1]); } }
    public PropertyValue<T13> Parameter13 { get { return this.Bl<T13>(AllProperties[13 + 1]); } }
    public PropertyValue<T14> Parameter14 { get { return this.Bl<T14>(AllProperties[14 + 1]); } }
  }
}
namespace Bling.WPF {
  using Bling.Linq;
  public class DistortBl {
    public PointBl UV;
    public ColorBl Color;
    // we don't have to specify a color!
    public static implicit operator DistortBl(PointBl p) {
      return new DistortBl() { UV = p };
    }
  }
  public partial class EffectBl {
    /// <summary>
    /// Create a custom pixel shader effect as a two argument function.
    /// </summary>
    /// <param name="F">The first argument is the texture of the UI element being shaded, while the second argument
    /// is the cooridinate of the pixel being shaded.</param>
    public EffectBl(Func<BrushBl, PointBl, ColorBl> F) : this(FromF(F(Defs.Input, Defs.UV))) { }

    protected Expr<PixelShader> PixelShader0 {
      get { return Underlying.Property<PixelShader>(BlingShaderEffect.PixelShaderProperty0); }
    }
    /// <summary>
    ///     Gets or sets a value indicating whether to use hardware or software rendering.
    ///     This is a dependency property.
    /// </summary>
    public EnumBl<ShaderRenderMode> ShaderRenderMode {
      get {
        return new DependencyPropertyValue<PixelShader, ShaderRenderMode>() {
          TargetValue = PixelShader0, ReadProperty = PixelShader.ShaderRenderModeProperty, 
          WriteProperty = PixelShader.ShaderRenderModeProperty,
        };
      }
      set {
        ShaderRenderMode.Bind = value;
      }
    }
    /// <summary>
    /// Execute pixel shader in software.
    /// </summary>
    public BoolBl IsSoftwareRender {
      get {
        return ShaderRenderMode.Underlying.Map<bool>(mode => mode == System.Windows.Media.Effects.ShaderRenderMode.SoftwareOnly,
          isSoft => isSoft ? System.Windows.Media.Effects.ShaderRenderMode.SoftwareOnly : System.Windows.Media.Effects.ShaderRenderMode.HardwareOnly);
      }
      set { IsSoftwareRender.Bind = value; }
    }
    private void DoCustom(Func<BrushBl, PointBl, ColorBl> value, bool DoTransform) {
      var color = value(Defs.Input, Defs.UV);
      var effect = FromF(color);
      this.Bind = effect;
      // now, try to disect color to find...
      if (DoTransform) {
        var distort = color.Underlying.Visit<PointBl>(new FindUV());
        if (((object)distort) != null) {
          if (!distort.Underlying.Identity.Equals(Defs.UV.Underlying.Identity)) {
            var distortB = ((CustomTransformEval.MyInfo<Point>)distort.Underlying.Eval(new CustomTransformEval() { 
              Effect = effect, 
            })).Value;

            var eval = new TransformEval();
            try {
              var resultB = ((TransformEval.MyInfo<Point>)distortB.Eval(eval)).Value;
              var func = eval.Eval0<Microsoft.Func<Point, Point>>(resultB);
              //Console.WriteLine(func.Dump);
              effect.CustomEffectMapping = p => func.Compile()(p);
            } catch (NotSupportedException e) {
              // do nothing
              ;
            }
          }
        }
      }
    }


    /// <summary>
    /// Apply a custom pixel shader effect to this UI element as a two argument function.
    /// The first argument is the texture of the UI element being shaded, while the second argument
    /// is the cooridinate of the pixel being shaded.
    /// </summary>
    public Func<BrushBl, PointBl, ColorBl> Custom {
      set {
        DoCustom(value, false);
      }
    }
    public Func<BrushBl, PointBl, ColorBl> CustomWithMap {
      set {
        DoCustom(value, true);
      }
    }
    

    private class FindUV :  TranslateEval<FindUV>, ExprVisitor<PointBl> {
      private readonly HashSet<Expr<Point>> Found = new HashSet<Expr<Point>>();
      public override Expr<S> Translate<S>(Expr<S> value) {
        if (value is Operation<Brush,Point,Color>) {
          var value0 = (Operation<Brush, Point, S>)value;
          if (value0.Op.Marker == Markers.Tex2DMarker && value0.Value1.Identity.Equals(Defs.Input.Underlying.Identity)) {
            Found.Add(value0.Value2);
            return (value0);  
          }
        }
        return base.Translate<S>(value);
      }
      public PointBl Visit<T>(Expr<T> value) {
        Translate(value);
        PointBl ret = null;
        foreach (var v in Found) ret = v;
        if (Found.Count == 1) return ret;
        else return null;
      }
    }
    private class CustomTransformEval : TranslateEval<CustomTransformEval> {
      public BlingShaderEffect Effect;
      public override Expr<T> Translate<T>(Expr<T> value) {
        if (Effect.Registers.ContainsKey(value)) {
          return (Expr<T>) Effect[Effect.Registers[value]];
        } else return base.Translate<T>(value);
      }

    }

    private class TransformEval : DefaultLinqEval, BaseShaderEval<DefaultLinqEval> {
      public readonly PointBl Param;
      public TransformEval() : base(new ParameterContext()) {
        Param = this.Parameters.NewParameter<Point>();
      }
      public Info<Point> Eval(CurrentPoint pt) {
        return Param.Underlying.Eval(this);
      }
      public Info<Brush> Eval(BrushInput input) {
        throw new NotSupportedException();
      }
    }
    private static BlingShaderEffect FromF(ColorBl color) {
      var eval = new ShaderEval();
      var shader0 = eval.GenerateA(color.Underlying);
      var shader1 = eval.GenerateB(shader0);
      shader1.Registers = eval.Registers;
      return shader1;
    }
    public static Func<T,Effect> Reuse<T>(Func<BrushBl, PointBl, Expr<T>, ColorBl> F) {
      var assign = new NowAssign<T>();
      var eval = new ShaderEval();
      var shader = eval.GenerateA(F(Defs.Input, Defs.UV, assign.Argument).Underlying);
      var f = eval.GenerateC<T>(shader, assign.Argument);
      return t => f(t);
    }
    public static Func<T, S, Effect> Reuse<T,S>(Func<BrushBl, PointBl, Expr<T>, Expr<S>, ColorBl> F) {
      var assign = new NowAssign<T,S>();
      var eval = new ShaderEval();
      var shader = eval.GenerateA(F(Defs.Input, Defs.UV, assign.ArgumentA, assign.ArgumentB).Underlying);
      var f = eval.GenerateC<T,S>(shader, assign.ArgumentA, assign.ArgumentB);
      return (t,s) => f(t,s);
    }
  }
  public abstract partial class FrameworkElementBl<T,BRAND> 
    where T : FrameworkElement 
    where BRAND : FrameworkElementBl<T,BRAND> {
    /// <summary>
    /// Apply a custom pixel shader effect to this UI element as a two argument function.
    /// The first argument is the texture of the UI element being shaded, while the second argument
    /// is the cooridinate of the pixel being shaded.
    /// </summary>
    [Obsolete("Set Effect.Custom instead.")]
    public Func<BrushBl,PointBl,ColorBl> CustomEffect {
      set { Effect.Custom = value; }
    }
    /// <summary>
    /// Same as Effect.Custom with additional argument for element that the custom effect is being applied to. 
    /// Useful for initializers that don't give you access to the object being created. 
    /// </summary>
    public Func<BRAND, BrushBl, PointBl, ColorBl> EffectCustom {
      set {
        Effect.Custom = (input, uv) => value((BRAND) this, input, uv);
      }
    }

  }
}
